package com.fary.boot.context.properties.source;

import com.fary.core.env.ConfigurableEnvironment;
import com.fary.core.env.PropertySource;
import com.fary.core.env.PropertySource.StubPropertySource;
import com.fary.util.Assert;
import com.fary.util.ConcurrentReferenceHashMap;
import com.fary.util.ConcurrentReferenceHashMap.ReferenceType;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.Function;

/**
 * 将 MutablePropertySources 转换成 ConfigurationPropertySource 的适配器
 *
 * MutablePropertySources 是迭代 PropertySource<?>
 * SpringConfigurationPropertySources 是迭代 ConfigurationPropertySource
 */
class SpringConfigurationPropertySources implements Iterable<ConfigurationPropertySource> {

    // MutablePropertySources
    private final Iterable<PropertySource<?>> sources;

    // key : PropertiesPropertySource
    // value: ConfigurationPropertySource
    private final Map<PropertySource<?>, ConfigurationPropertySource> cache = new ConcurrentReferenceHashMap<>(16,
            ReferenceType.SOFT);

    SpringConfigurationPropertySources(Iterable<PropertySource<?>> sources) {
        Assert.notNull(sources, "Sources must not be null");
        this.sources = sources;
    }

    /**
     * for循环遍历
     * 获取迭代器
     */
    @Override
    public Iterator<ConfigurationPropertySource> iterator() {
        return new SourcesIterator(this.sources.iterator(), this::adapt);
    }

    /**
     * 适配
     * PropertySource --> ConfigurationPropertySource
     */
    private ConfigurationPropertySource adapt(PropertySource<?> source) {
        // 先从缓存中获取
        ConfigurationPropertySource result = this.cache.get(source);
        // 大多数 PropertySources 仅使用源名称测试相等性，因此我们需要检查实际源是否也没有更改。
        if (result != null && result.getUnderlyingSource() == source) {
            return result;
        }
        // 获取 ConfigurationPropertySource
        result = SpringConfigurationPropertySource.from(source);
        // 放入缓存中
        this.cache.put(source, result);
        return result;
    }

    /**
     * 具体迭代逻辑
     */
    private static class SourcesIterator implements Iterator<ConfigurationPropertySource> {

        // 资源真正的迭代器集合
        private final Deque<Iterator<PropertySource<?>>> iterators;

        // 迭代获取的下一个值
        private ConfigurationPropertySource next;

        // 迭代的逻辑
        private final Function<PropertySource<?>, ConfigurationPropertySource> adapter;

        SourcesIterator(Iterator<PropertySource<?>> iterator, Function<PropertySource<?>, ConfigurationPropertySource> adapter) {
            this.iterators = new ArrayDeque<>(4);
            this.iterators.push(iterator);
            this.adapter = adapter;
        }

        @Override
        public boolean hasNext() {
            return fetchNext() != null;
        }

        @Override
        public ConfigurationPropertySource next() {
            ConfigurationPropertySource next = fetchNext();
            if (next == null) {
                throw new NoSuchElementException();
            }
            this.next = null;
            return next;
        }

        /**
         * 获取下一个元素
         */
        private ConfigurationPropertySource fetchNext() {
            // 在判断是否有下一个元素时，this.next == null
            // 真正获取next的时候，return this.next;
            if (this.next == null) {
                // 迭代器集合为空
                if (this.iterators.isEmpty()) {
                    return null;
                }
                // 获取迭代器，进行迭代判断是否有下一个元素
                if (!this.iterators.peek().hasNext()) {
                    // 如果没有下一个元素，则移除第一个迭代器，继续递归
                    this.iterators.pop();
                    return fetchNext();
                }
                // 获取下一个元素
                PropertySource<?> candidate = this.iterators.peek().next();
                // 如果是 ConfigurableEnvironment
                if (candidate.getSource() instanceof ConfigurableEnvironment) {
                    // 放入迭代器
                    push((ConfigurableEnvironment) candidate.getSource());
                    return fetchNext();
                }
                // 是否忽略当前 PropertySource
                // StubPropertySource 和 ConfigurationPropertySourcesPropertySource 忽略，继续获取下一个
                if (isIgnored(candidate)) {
                    return fetchNext();
                }
                this.next = this.adapter.apply(candidate);
            }
            return this.next;
        }

        private void push(ConfigurableEnvironment environment) {
            this.iterators.push(environment.getPropertySources().iterator());
        }

        private boolean isIgnored(PropertySource<?> candidate) {
            return (candidate instanceof StubPropertySource || candidate instanceof ConfigurationPropertySourcesPropertySource);
        }

    }

}